#ifndef _BLK_H
#define _BLK_H

#define NR_BLK_DEV	7
/*
 * NR_REQUEST is the number of entries in the request-queue.
 * NOTE that writes may use only the low 2/3 of these: reads
 * take precedence.
 *
 * 32 seems to be a reasonable number: enough to get some benefit
 * from the elevator-mechanism, but not so much as to lock a lot of
 * buffers when they are in the queue. 64 seems to be too many (easily
 * long pauses in reading when heavy writing/syncing is going on)
 */
// 请求队列中所包含的项数
#define NR_REQUEST	32

/*
 * Ok, this is an expanded form so that we can use the same
 * request for paging requests when that is implemented. In
 * paging, 'bh' is NULL, and 'waiting' is used to wait for
 * read/write completion.
 */
// 请求队列中项的结构。表示一个设备请求
// 其中如果字段 dey=-1，则表示队列中该项没有被使用。
// 字段 cmd 可取常量 READ (0） 或 WRITE(1)(定义在 include/linux/fs.h 第26 行开始处）
struct request {
	int dev;		/* -1 if no request 分区设备号，逻辑设备号，例如300,301,302...305,306... */
	int cmd;		/* READ or WRITE */
	int errors;							/* 操作时产生的错误次数 */
	unsigned long sector;				/* （当前分区中的）起始扇区 */
	unsigned long nr_sectors;			/* 本请求项需要读/写扇区数 */
	char * buffer;						/* 请求项存放数据的数据缓冲区 */
	struct task_struct * waiting;		/* 等待该请求项的进程，没用到？？？ */
	struct buffer_head * bh;			/* 缓冲区块头指针 */
	struct request * next;				/* 指向下一请求项 */
};

/*
 * This is used in the elevator algorithm: Note that
 * reads always go before writes. This is natural: reads
 * are much more time-critical than writes.
 */
/*
read请求排在写请求前面，先处理读请求，再处理写请求；
同一读或写请求，先处理设备号小的设备请求，再处理设备号大的设备请求；
同一读或写请求，同一设备，按先里面的扇区再到外面的扇区的顺序处理。

1. 先比较读写操作，读操作(0)优于写操作(1)；
2. 如果读写操作相同时，则对比设备号。设备号小的优先；
3. 如果读写操作、设备号都相同时，则对比扇区号。扇区号小的优先；
*/
// s1是否优先s2（扇区号越小越优先。如果 s1 < s2 ，返回true ）
#define IN_ORDER(s1,s2) \
((s1)->cmd<(s2)->cmd || ((s1)->cmd==(s2)->cmd && \
((s1)->dev < (s2)->dev || ((s1)->dev == (s2)->dev && \
(s1)->sector < (s2)->sector))))

// 块设备结构，表示某一种块设备
struct blk_dev_struct {
	void (*request_fn)(void);			// 处理当前请求的函数
	struct request * current_request;   // 该块设备当前正在处理的请求信息结构
};

extern struct blk_dev_struct blk_dev[NR_BLK_DEV];  /* 块设备表（数组），每种块设备占用一项。*/
extern struct request request[NR_REQUEST];     /* 请求项队列数组 */
extern struct task_struct * wait_for_request;  /* sleep_on 等待队列头指针 */

#ifdef MAJOR_NR

/*
 * Add entries as needed. Currently the only block devices
 * supported are hard-disks and floppies.
 */

#if (MAJOR_NR == 1)
/* ram disk */
#define DEVICE_NAME "ramdisk"
#define DEVICE_REQUEST do_rd_request
#define DEVICE_NR(device) ((device) & 7)  // 取值：0-7
#define DEVICE_ON(device) 
#define DEVICE_OFF(device)

#elif (MAJOR_NR == 2)
/* floppy */
#define DEVICE_NAME "floppy"
#define DEVICE_INTR do_floppy
#define DEVICE_REQUEST do_fd_request
#define DEVICE_NR(device) ((device) & 3)
#define DEVICE_ON(device) floppy_on(DEVICE_NR(device))
#define DEVICE_OFF(device) floppy_off(DEVICE_NR(device))

#elif (MAJOR_NR == 3)
/* harddisk */
#define DEVICE_NAME "harddisk"
#define DEVICE_INTR do_hd
#define DEVICE_REQUEST do_hd_request
#define DEVICE_NR(device) (MINOR(device)/5)
#define DEVICE_ON(device)
#define DEVICE_OFF(device)  // 硬盘一直在工作，无须开启和关闭。

// Linux0.11中：
// 逻辑设备号 = 主设备号 * 256 + 次设备号
// 也即 dev_no = (major << 8) + minor

#else
/* unknown blk device */
#error "unknown blk device"

#endif

#define CURRENT (blk_dev[MAJOR_NR].current_request)  /* (指定主设备号的)当前请求项（指针） */
#define CURRENT_DEV DEVICE_NR(CURRENT->dev) /* 当前请求项对应的整盘号，/dev/hdX中的X，可以是0或1 */

#ifdef DEVICE_INTR
void (*DEVICE_INTR)(void) = NULL;  /* 声明一个全局变量（函数指针），在hd_out()中重新赋值 */
#endif
static void (DEVICE_REQUEST)(void);

// 解锁指定的缓冲区（块）。
// 如果指定的缓冲区 bh 并没有被上锁，则显示警告信息。
// 否则将该缓冲区解锁，并唤醒等待该缓冲区的进程。
// 参数是缓冲区头指针。
static inline void unlock_buffer(struct buffer_head * bh)
{
	if (!bh->b_lock)
		printk(DEVICE_NAME ": free buffer being unlocked\n");
	bh->b_lock=0;
	wake_up(&bh->b_wait);
}

// 对本次请求项作结束处理：结束本次请求项处理，从请求项链表中删除本请求项，并将当前请求项CURRENT指向下一个请求项
static inline void end_request(int uptodate)
{
	// CURRENT 为当前请求结构项指针
	// 关闭设备
	DEVICE_OFF(CURRENT->dev);
	// 检查此次读写缓冲区是否有效。如果有效则根据参数值设置缓冲区数据更新标志，并解锁该缓冲区。
	if (CURRENT->bh) {
		// 置更新标志。
		CURRENT->bh->b_uptodate = uptodate; 
		// 解锁缓冲区
		unlock_buffer(CURRENT->bh);
	}
	//若更新标志为0，则显示出错信息
	if (!uptodate) {
		printk(DEVICE_NAME " I/O error\n\r");
		printk("dev %04x, block %d\n\r",CURRENT->dev,
			CURRENT->bh->b_blocknr);
	}
	// 唤醒等待该请求项的进程。
	wake_up(&CURRENT->waiting);
	// 唤醒等待空闲请求项的进程。
	wake_up(&wait_for_request);
	// 释放该请求项。
	CURRENT->dev = -1;
	// 从请求链表中删除该请求项，并且当前请求项指针指向下一个请求项。
	CURRENT = CURRENT->next;
}

// 初始化请求项（检测请求项的合法性）
// 如果设备当前请求项为空（NULL），表示本设备目前己无需要处理的请求项。于是退出函数。
// 否则，如果当前请求项中设备的主设备号不等于驱动程序定义的主设备号，说明请求项队列乱掉了，于是内核显示出错信息并停机。
// 否则，若请求项中用的缓冲块没有被锁定，也说明内核程序出了问题，于是显示出错信息并停机。
#define INIT_REQUEST \
repeat: \
	if (!CURRENT) \
		return; \
	if (MAJOR(CURRENT->dev) != MAJOR_NR) \
		panic(DEVICE_NAME ": request list destroyed"); \
	if (CURRENT->bh) { \
		if (!CURRENT->bh->b_lock) \
			panic(DEVICE_NAME ": block not locked"); \
	}

#endif

#endif
